#include "../include/buffer.h"
#include <string.h>

buffer_data_t *create_buffer_data(alloc_function allocator,
                                  dealloc_function deallocator,
                                  size_t opicity) {
  // 分配结构
  buffer_data_t *ret = (buffer_data_t *)allocator(sizeof(buffer_data_t));
  if (!ret) {
    return NULL;
  }

  // 分配数据
  ret->data = allocator(opicity);
  if (!ret->data) {
    deallocator(ret);
    return NULL;
  }

  // 设定头尾
  ret->head = ret->data;
  ret->tail = ret->data;
  ret->next = NULL;
  ret->opicity = opicity;
  return ret;
}

void free_buffer_data(buffer_data_t *bf_ptr, dealloc_function deallocator) {
  // 释放buffer_data
  buffer_data_t *temp= NULL;
  do {
    // 回收数据
    if (bf_ptr->data) {
      deallocator(bf_ptr->data);
    }

    temp = bf_ptr;
    bf_ptr = bf_ptr->next;

    // 回收结构
    deallocator(temp);
  } while (bf_ptr);
}

size_t get_single_buffer_size(buffer_data_t *bf) {
  if (bf->tail > bf->head) {
    return bf->tail - bf->head;
  }
  return 0;
}

size_t get_buffer_data_size(buffer_data_t *bf_ptr) {
  size_t ret = 0;
  do {
    ret += get_single_buffer_size(bf_ptr);
    bf_ptr = bf_ptr->next;
  } while (bf_ptr);
  return ret;
}

size_t buffer_data_last_size(buffer_data_t *bf_ptr) {
  return bf_ptr->opicity - (bf_ptr->tail - bf_ptr->data);
}

char *buffer_data_get(buffer_data_t *bf_ptr, uint64_t index) {
  size_t size = 0;
  do {
    size = get_single_buffer_size(bf_ptr);
    if (index < size) {
      return bf_ptr->head + index;
    }
    index -= size;
    bf_ptr = bf_ptr->next;
  } while (bf_ptr);

  return NULL;
}

void buffer_add_size(buffer_data_t *bf_ptr, size_t value) {
  size_t size = 0;
  do {
    size = buffer_data_last_size(bf_ptr);
    if (size > 0) {
      if (size >= value) {
        bf_ptr->tail += value;
        break;
      } else {
        bf_ptr->tail += size;
        value -= size;
      }
    }
    bf_ptr = bf_ptr->next;
  } while (bf_ptr);
}

size_t buffer_data_find(buffer_data_t *bf_ptr, const char *t, size_t t_size) {
  size_t bf_size = get_buffer_data_size(bf_ptr);
  if (bf_size < t_size) {
    return 0;
  }

  for (int64_t i = 0; i <= bf_size - t_size; ++i) {
    int found = 1;
    for (size_t j = 0; j < t_size; ++j) {
      if (*buffer_data_get(bf_ptr, i + j) != *(t + j)) {
        found = 0;
        break;
      }
    }

    if (found) {
      return i + t_size;
    }
  }

  return 0;
}

int build_buffer_data(buffer_data_t **bf_ptr, size_t size,
                      alloc_function allocator, dealloc_function deallocator,
                      char **build_ret, int *need_release) {
  // 查看第一组数据是否够用
  if (get_single_buffer_size((*bf_ptr)) >= size) {
    *build_ret = (*bf_ptr)->head;
    (*bf_ptr)->head += size;
    return 1;
  }

  // 如果不够用则需要组合数据
  // 分配数据
  size_t copyed_size = 0;
  *build_ret = (char *)allocator(size);
  if (!(*build_ret)) {
    return 0;
  }

  do {
    // 计算当前buffer大小
    size_t cur_size = get_single_buffer_size((*bf_ptr));

    // 如果当前数据够用
    if (cur_size > size - copyed_size) {
      // 拷贝数据
      memcpy((*build_ret) + copyed_size, (*bf_ptr)->head, size - copyed_size);
      // 将当前buffer的头部后移
      (*bf_ptr)->head += size - copyed_size;
      // 返回数据
      return 1;
    } else {
      // 拷贝数据
      memcpy((*build_ret) + copyed_size, (*bf_ptr)->head, cur_size);
      copyed_size += cur_size;
      // 获取下一个buffer
      buffer_data_t *temp = (*bf_ptr)->next;
      // 如果还有buffer
      if (temp) {
        // 释放该buffer
        (*bf_ptr)->next = NULL;
        free_buffer_data((*bf_ptr), deallocator);
        // 设置当前数据
        (*bf_ptr) = temp;
      }
      // 否则清空该buffer
      else {
        (*bf_ptr)->head = (*bf_ptr)->data;
        (*bf_ptr)->tail = (*bf_ptr)->data;
        (*bf_ptr) = NULL;
      }

      if (copyed_size >= size) {
        return 1;
      }
    }
  } while ((*bf_ptr));
  return 0;
}

int get_buffer(alloc_function allocator, dealloc_function deallocator,
               buffer_data_t *bf, char **ret, size_t *size) {
  do {
    size_t last_size = buffer_data_last_size(bf);
    if (last_size > 0) {
      *ret = bf->tail;
      *size = last_size;
      return 1;
    }

    if (!bf->next) {
      bf->next = create_buffer_data(allocator, deallocator, bf->opicity);
      if (!bf->next) {
        break;
      }
    }
    bf = bf->next;
  } while (bf);

  return 0;
}
